# 闭包：closure
# def func_a():
#     x = 880
#
#     # 在函数中嵌套一个函数，该函数无法被外部调用
#     def func_b():
#         print(x)
#
#     # 只能在 func_a 中调用 func_b 函数
#     func_b()
#
#
# func_a()


# 问题：可以不通过 func_a 函数来调用 func_b 吗？


# 改造
def func_wai():
    x = 880

    def func_nei():
        print(x)

    # 将 func_nei 作为 外部函数的返回值
    return func_nei  # 将函数作为返回值的时候千万不能带上括号！！！


# 也可以这样写
func_wai()()  # 第一个括号表示调用外层函数，第二个括号表示调用返回的函数（内层函数）

funny = func_wai()
print(type(funny))  # <class 'function'>

# 执行返回的函数
funny()  # 880


# 对于嵌套函数来说，外层函数的作用域是可以通过某种形式给保存下来的，比如这个 func_wai 中的变量 x， 通过变量 funny 保存下来了

# 闭包：也叫做工厂函数

def power(exp):
    def exp_of(base):
        # 幂运算
        # nonlocal exp
        return base ** exp

    return exp_of


square = power(2)
cube = power(3)

print(square(2))  # 4
print(square(5))  # 25

print(cube(2))  # 8
print(cube(3))  # 27


def outer():
    x = 0
    y = 0

    def inner(x1, y1):
        nonlocal x, y
        x += x1
        y += y1
        print(F"现在，x = {x}，y = {y}")

    return inner


move = outer()
move(0, 0)  # 现在，x = 0，y = 0
move(1, 2)  # 现在，x = 1，y = 2
move(-2, 2)  # 现在，x = -1，y = 4
#  x,y两个变量被记忆了，2次调用x,y的值被保留了
move(1, -4)  # 现在，x = 0，y = 0

# 上面的函数可以在游戏开发中使用，保存角色的移动数据


"""
    实现角色的移动函数
"""
origin = (0, 0)  # 这个是原点
legal_x = [-100, 100]  # 限定 x 轴的移动范围
legal_y = [-100, 100]  # 限定 y 轴的移动范围


def create(pos_x=0, pos_y=0):
    def moving(direction, step):
        nonlocal pos_x, pos_y
        # 先计算移动之后的坐标
        new_x = pos_x + direction[0] * step
        new_y = pos_y + direction[1] * step

        # 再进行边界校验
        if new_x < legal_x[0]:
            pos_x = legal_x[0] - (new_x - legal_x[0])
        elif new_x > legal_x[1]:
            pos_x = legal_x[1] - (new_x - legal_x[1])
        else:
            pos_x = new_x

        if new_y < legal_y[0]:
            pos_y = legal_y[0] - (new_y - legal_y[0])
        elif new_y > legal_y[1]:
            pos_y = legal_y[1] - (new_y - legal_y[1])
        else:
            pos_y = new_y

        return pos_x, pos_y

    return moving


move = create()

# 向右移动20步
pos = move([1, 0], 20)
print("向右移动20步的位置是:", pos)

# 向上移动120步
pos = move([0, 1], 120)
print("向上移动120步:", pos)

# 向右下移动88步
pos = move([1, -1], 88)
print("向右下移动88步:", pos)

# 闭包总结：利用嵌套函数的外层作用域具有记忆能力这个特性,让数据保存在外层函数的参数或者变量中
# 将内存函数作为返回值给返回，这样就可以从外部间接的调用到内存的函数
